#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "tda_sheet.h"
#include "common.h"

const float notes_1st_octave [MAX_NOTES]={
	27.5,
	29.13525,
	30.86775,
	32.70325,
	34.647875,
	36.708125,
	38.890875,
	41.203625,
	43.6535,
	46.24925,
	48.999375,
	51.913125
	};
	

status_t adt_sheet_new (adt_sheet_t ** pp_adt_sheet, FILE * sheet_data){

	status_t status;
	if(pp_adt_sheet == NULL || sheet_data==NULL) return ERROR_NULL_POINTER;
	
	if((*pp_adt_sheet=(adt_sheet_t*)malloc(sizeof(adt_sheet_t)))==NULL) return ERROR_MEMORY;
	
	adt_sheet_set_notes_qty(*pp_adt_sheet,0);
	(*pp_adt_sheet)->sheet=NULL;
	
	if((status=adt_sheet_load_data(*pp_adt_sheet,sheet_data))!=OK){
		adt_sheet_destroy(pp_adt_sheet);
		return status;
	}
	return OK;
}

status_t adt_sheet_load_data (adt_sheet_t * p_adt_sheet, FILE * fi){
	
	string_t line;
	note_struct_t * p_aux_note_struct_t;
	char * p_aux;
	char aux_char;
	float aux_float, aux_start_t, aux_length;
	float total_t=0;
	status_t status;
	size_t alloc_size, used_size=0;
	
	
	if( p_adt_sheet==NULL || fi==NULL) return ERROR_NULL_POINTER;
	
	if((p_adt_sheet->sheet=(note_struct_t *)malloc(INIT_CHOP*sizeof(note_struct_t)))==NULL) return ERROR_MEMORY;
	alloc_size=INIT_CHOP;
	
	while(fgets(line,MAX_STRING,fi)!=NULL){
		if(used_size==alloc_size){
			if((p_aux_note_struct_t=(note_struct_t *)realloc(p_adt_sheet->sheet,(used_size+CHOP_SIZE)*sizeof(note_struct_t)))==NULL){
				return ERROR_MEMORY;
			}
			p_adt_sheet->sheet=p_aux_note_struct_t;
			alloc_size+=CHOP_SIZE;
		}
		
		/* carga del tiempo inicial*/
		
		if((p_aux=strtok(line,TOKEN_SPACE))==NULL) return ERROR_INVALID_DATA;
		aux_start_t=strtod(p_aux,&p_aux);
		if(*p_aux!=STR_END || aux_start_t < 0) return ERROR_INVALID_DATA;
		if((status=adt_sheet_set_start_t(p_adt_sheet,aux_start_t,used_size))!=OK){
			return status;	
		}
		
		/* carga de la nota y la octava*/
		
		if((p_aux=strtok(NULL,TOKEN_SPACE))==NULL) return ERROR_INVALID_DATA;
		if((status=adt_sheet_set_note(p_adt_sheet,p_aux,used_size))!=OK){
			return status;	
		}
		
		/* carga de la duracion*/
		
		if((p_aux=strtok(NULL,TOKENS_END_LINE))==NULL) return ERROR_INVALID_DATA;
		aux_length=strtod(p_aux,&p_aux);
		if(((aux_char=*p_aux)!=STR_END && aux_char!=CHAR_LF) || aux_length < 0) return ERROR_INVALID_DATA;
		if((status=adt_sheet_set_length(p_adt_sheet,aux_length,used_size))!=OK){
			return status;	
		}
		
		/*refresco de tiempo total*/
		
		aux_float=aux_start_t + aux_length;
		if(aux_float>total_t) total_t=aux_float;
		used_size++;
	}
	
	if((status=adt_sheet_set_total_t(p_adt_sheet,total_t))!=OK){
		return status;	
	}
	if((status=adt_sheet_set_notes_qty(p_adt_sheet,used_size))!=OK){
		return status;	
	}	
	/*se puede agregar un realloc descendente*/
	
	return OK;
}

status_t adt_sheet_set_start_t(adt_sheet_t * p_adt_sheet,float time,size_t n_note){
	
	if(p_adt_sheet==NULL) return ERROR_NULL_POINTER;
	if(time<0 || n_note<0) return ERROR_INVALID_DATA;
	
	p_adt_sheet->sheet[n_note].start_t=time;
	return OK;
}

status_t adt_sheet_set_note(adt_sheet_t * p_adt_sheet,char * s,size_t n_note){
	
	int aux_octave=0;
	char aux_char;
	char * p_octave, *p_aux;
	unsigned char aux_note;
	size_t i;
	float aux_float;
	status_t status;
	
	if(p_adt_sheet==NULL || s==NULL) return ERROR_NULL_POINTER;
	if(n_note<0) return ERROR_INVALID_DATA;
	
	
	if((aux_char=s[0]) < 'A' || aux_char > 'G' ) return ERROR_INVALID_DATA;
	aux_note = aux_char - 'A';
	if(aux_note>=1)aux_note++; /* sumo 1 a la variable aux_note para saltear las notas con sostenido*/
	if(aux_note>=4)aux_note++;
	if(aux_note>=6)aux_note++;
	if(aux_note>=9)aux_note++;
	
	aux_char=s[1];
	switch(aux_char){
		case SOST_FLAG:	
			aux_note++;
			p_octave=s+2;
			break;
		case BEMOL_FLAG:
			
			p_octave=s+2;
			if(aux_note==A){  		/*caso en el cual la nota es A bemol, por lo que la octaba real es la anterior a la que se encuentra en el documento*/
				aux_octave--;
				aux_note=Gs;
			} else{
				aux_note--;
			}
			
			break;
		default:
			p_octave=s+1;
	}
	aux_octave+=strtol(p_octave,&p_aux,10);
	if((*p_aux)!=STR_END) return ERROR_INVALID_DATA;
	
	if (aux_octave<MIN_OCTAVE || aux_octave > MAX_OCTAVE) return ERROR_INVALID_DATA;
	p_adt_sheet->sheet[n_note].note=aux_note;
	p_adt_sheet->sheet[n_note].octave=aux_octave;
	
	/*calculo frecuencia*/
	
	aux_float=notes_1st_octave[aux_note];
	for(i=0;i<aux_octave-1;i++){
		aux_float*=2;
	}
	if((status=adt_sheet_set_frequency(p_adt_sheet,aux_float,n_note))!=OK){
		return status;
	}
	
	return OK;
}

status_t adt_sheet_set_length(adt_sheet_t * p_adt_sheet,float time,size_t n_note){
	
	if(p_adt_sheet==NULL) return ERROR_NULL_POINTER;
	if (time<0 || n_note < 0) return ERROR_INVALID_DATA;
	
	p_adt_sheet->sheet[n_note].length=time;
		
	return OK;
}

status_t adt_sheet_set_frequency(adt_sheet_t * p_adt_sheet,float frequency,size_t n_note){
	if(p_adt_sheet==NULL) return ERROR_NULL_POINTER;
	if (frequency<=0 || n_note < 0) return ERROR_INVALID_DATA;
	
	p_adt_sheet->sheet[n_note].frequency=frequency;
		
	return OK;
}
status_t adt_sheet_set_total_t(adt_sheet_t * p_adt_sheet,float total_t){
	if(p_adt_sheet==NULL) return ERROR_NULL_POINTER;
	if (total_t<=0) return ERROR_INVALID_DATA;
	
	p_adt_sheet->total_t=total_t;
		
	return OK;
}
status_t adt_sheet_set_notes_qty(adt_sheet_t * p_adt_sheet,int notes_qty){
	if(p_adt_sheet==NULL) return ERROR_NULL_POINTER;
	if (notes_qty<=0) return ERROR_INVALID_DATA;
	
	p_adt_sheet->notes_qty=notes_qty;
	
	return OK;
}

status_t adt_sheet_destroy(adt_sheet_t ** pp_adt_sheet){ 

	if(pp_adt_sheet==NULL) return ERROR_NULL_POINTER;
	
	if ((*pp_adt_sheet)->sheet!=NULL){
		free((*pp_adt_sheet)->sheet);
	}
	free(*pp_adt_sheet);
	pp_adt_sheet=NULL;
	return OK;
}

int adt_sheet_get_notes_qty(adt_sheet_t * p_adt_sheet){
	return p_adt_sheet->notes_qty;
}

float adt_sheet_get_start_t(adt_sheet_t * p_adt_sheet,size_t n_note){
	return p_adt_sheet->sheet[n_note].start_t;
}

float adt_sheet_get_length(adt_sheet_t * p_adt_sheet,size_t n_note){
	return p_adt_sheet->sheet[n_note].length;
}

unsigned char adt_sheet_get_octave(adt_sheet_t * p_adt_sheet,size_t n_note){
	return p_adt_sheet->sheet[n_note].octave;
}

notes_t adt_sheet_get_note(adt_sheet_t * p_adt_sheet,size_t n_note){
	return p_adt_sheet->sheet[n_note].note;
}
float adt_sheet_get_frequency(adt_sheet_t * p_adt_sheet,size_t n_note){
	return p_adt_sheet->sheet[n_note].frequency;
}
float adt_sheet_get_total_t(adt_sheet_t * p_adt_sheet){
	return p_adt_sheet->total_t;
}
